#include "ModelUpdater.h"

#include <folly/logging/xlog.h>

namespace nexus {

ModelUpdater::ModelUpdater(Pipeline& pipeline, Network& network)
    : pipeline_(pipeline), network_(network) {}

void ModelUpdater::run() {
  while (1) {
    std::shared_ptr<AtomicOverwrite> ao;
    pipeline_.blockingRead(ao);
    process(ao);
  }
}

void ModelUpdater::process(std::shared_ptr<AtomicOverwrite> ao) {
  auto device = ao->device;

  bool inc = false;
  if (inc) {
    for (auto& o : ao->overwrites) {
      appendPredicate(device->network()->aps(), o.first);
    }
  } else {
    for (auto& o : ao->overwrites) {
      device->eportToHs()[o.second] = device->eportToHs()[o.second] | o.first;
    }
    for (auto& [devn, dev] : device->network()->nameToDevice()) {
      for (auto& [pn, hs] : dev.eportToHs()) {
        appendPredicate(device->network()->aps(), hs);
      }
    }
  }
  XLOG(INFO) << device->network()->aps().size();
}

void ModelUpdater::appendPredicate(std::set<HeaderSpace>& aps,
                                   HeaderSpace pred) {
  if (aps.size() == 0) {
    if (pred.isFalse() || pred.isTrue()) {
      aps.insert(HeaderSpace::hsAll());
    } else {
      aps.insert(pred);
      aps.insert(!pred);
    }
    return;
  }

  std::set<HeaderSpace> toRemove;
  std::set<HeaderSpace> toAdd;
  for (auto& ap : aps) {
    if (pred.isFalse()) {
      break;
    }
    auto p = ap & pred;
    if (!p.isFalse()) {
      if (p == ap) {
        continue;
      } else {
        toRemove.insert(ap);
        toAdd.insert(p);

        auto np = ap - p;
        toAdd.insert(np);

        pred -= p;
      }
    }
  }
  for (auto& p : toRemove) {
    aps.erase(p);
  }
  for (auto& p : toAdd) {
    aps.insert(p);
  }
}

void ModelUpdater::findCover(HeaderSpace& hs, std::set<HeaderSpace>& apset,
                             std::set<HeaderSpace>& cover) {
  for (auto& ap : apset) {
    if (!(ap & hs).isFalse()) {
      cover.insert(ap);
    }
  }
}

}  // namespace nexus